{
 "cells": [
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "439df52a-dadf-4fd8-a0ad-e59bac7ca529",
   "metadata": {},
   "source": [
    "# 使用 LangChain 构建一个 RAG 应用\n",
    "\n",
    "## RAG 是什么\n",
    "\n",
    "RAG 是一种将检索到的文档上下文与大语言模型（LLM）结合起来生成答案的技术。\n",
    "\n",
    "整个过程主要分为以下几个步骤：\n",
    "\n",
    "1. 加载文档：将原始数据(来源可能是在线网站、本地文件、各类平台等)加载到 LangChain 中。\n",
    "1. 文档分割：将加载的文档分割成较小的块，以适应模型的上下文窗口，并更容易进行向量嵌入和检索。\n",
    "1. 存储嵌入：将分割后的文档内容嵌入到向量空间，并存储到向量数据库中，以便后续检索。\n",
    "1. 检索文档：通过查询向量数据库，检索与问题最相关的文档片段。\n",
    "1. 生成回答：将检索到的文档片段与用户问题组合，生成并返回答案。\n",
    "\n",
    "通过这些步骤，可以构建一个强大的问答系统，将复杂任务分解为更小的步骤并生成详细回答。\n",
    "\n",
    "![rag](../images/rag.png)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b1d81c83-4083-4ec1-ac4c-e96840024278",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "06a5b694-e332-4311-a20f-4e55fc9e3cb3",
   "metadata": {
    "collapsed": true,
    "jupyter": {
     "outputs_hidden": true
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Requirement already satisfied: langchain in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (0.2.14)\n",
      "Requirement already satisfied: langchain_community in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (0.2.0)\n",
      "Requirement already satisfied: langchain_chroma in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (0.1.3)\n",
      "Requirement already satisfied: PyYAML>=5.3 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from langchain) (6.0.1)\n",
      "Requirement already satisfied: SQLAlchemy<3,>=1.4 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from langchain) (2.0.29)\n",
      "Requirement already satisfied: aiohttp<4.0.0,>=3.8.3 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from langchain) (3.9.3)\n",
      "Requirement already satisfied: async-timeout<5.0.0,>=4.0.0 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from langchain) (4.0.3)\n",
      "Requirement already satisfied: langchain-core<0.3.0,>=0.2.32 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from langchain) (0.2.32)\n",
      "Requirement already satisfied: langchain-text-splitters<0.3.0,>=0.2.0 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from langchain) (0.2.0)\n",
      "Requirement already satisfied: langsmith<0.2.0,>=0.1.17 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from langchain) (0.1.86)\n",
      "Requirement already satisfied: numpy<2,>=1 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from langchain) (1.26.4)\n",
      "Requirement already satisfied: pydantic<3,>=1 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from langchain) (2.6.4)\n",
      "Requirement already satisfied: requests<3,>=2 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from langchain) (2.31.0)\n",
      "Requirement already satisfied: tenacity!=8.4.0,<9.0.0,>=8.1.0 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from langchain) (8.2.3)\n",
      "Requirement already satisfied: dataclasses-json<0.7,>=0.5.7 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from langchain_community) (0.6.4)\n",
      "Requirement already satisfied: chromadb!=0.5.4,!=0.5.5,<0.6.0,>=0.4.0 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from langchain_chroma) (0.4.24)\n",
      "Requirement already satisfied: fastapi<1,>=0.95.2 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from langchain_chroma) (0.110.0)\n",
      "Requirement already satisfied: aiosignal>=1.1.2 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from aiohttp<4.0.0,>=3.8.3->langchain) (1.3.1)\n",
      "Requirement already satisfied: attrs>=17.3.0 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from aiohttp<4.0.0,>=3.8.3->langchain) (23.2.0)\n",
      "Requirement already satisfied: frozenlist>=1.1.1 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from aiohttp<4.0.0,>=3.8.3->langchain) (1.4.1)\n",
      "Requirement already satisfied: multidict<7.0,>=4.5 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from aiohttp<4.0.0,>=3.8.3->langchain) (6.0.5)\n",
      "Requirement already satisfied: yarl<2.0,>=1.0 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from aiohttp<4.0.0,>=3.8.3->langchain) (1.9.4)\n",
      "Requirement already satisfied: build>=1.0.3 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from chromadb!=0.5.4,!=0.5.5,<0.6.0,>=0.4.0->langchain_chroma) (1.1.1)\n",
      "Requirement already satisfied: chroma-hnswlib==0.7.3 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from chromadb!=0.5.4,!=0.5.5,<0.6.0,>=0.4.0->langchain_chroma) (0.7.3)\n",
      "Requirement already satisfied: uvicorn>=0.18.3 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from uvicorn[standard]>=0.18.3->chromadb!=0.5.4,!=0.5.5,<0.6.0,>=0.4.0->langchain_chroma) (0.23.2)\n",
      "Requirement already satisfied: posthog>=2.4.0 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from chromadb!=0.5.4,!=0.5.5,<0.6.0,>=0.4.0->langchain_chroma) (3.5.0)\n",
      "Requirement already satisfied: typing-extensions>=4.5.0 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from chromadb!=0.5.4,!=0.5.5,<0.6.0,>=0.4.0->langchain_chroma) (4.9.0)\n",
      "Requirement already satisfied: pulsar-client>=3.1.0 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from chromadb!=0.5.4,!=0.5.5,<0.6.0,>=0.4.0->langchain_chroma) (3.4.0)\n",
      "Requirement already satisfied: onnxruntime>=1.14.1 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from chromadb!=0.5.4,!=0.5.5,<0.6.0,>=0.4.0->langchain_chroma) (1.17.1)\n",
      "Requirement already satisfied: opentelemetry-api>=1.2.0 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from chromadb!=0.5.4,!=0.5.5,<0.6.0,>=0.4.0->langchain_chroma) (1.23.0)\n",
      "Requirement already satisfied: opentelemetry-exporter-otlp-proto-grpc>=1.2.0 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from chromadb!=0.5.4,!=0.5.5,<0.6.0,>=0.4.0->langchain_chroma) (1.23.0)\n",
      "Requirement already satisfied: opentelemetry-instrumentation-fastapi>=0.41b0 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from chromadb!=0.5.4,!=0.5.5,<0.6.0,>=0.4.0->langchain_chroma) (0.44b0)\n",
      "Requirement already satisfied: opentelemetry-sdk>=1.2.0 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from chromadb!=0.5.4,!=0.5.5,<0.6.0,>=0.4.0->langchain_chroma) (1.23.0)\n",
      "Requirement already satisfied: tokenizers>=0.13.2 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from chromadb!=0.5.4,!=0.5.5,<0.6.0,>=0.4.0->langchain_chroma) (0.15.2)\n",
      "Requirement already satisfied: pypika>=0.48.9 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from chromadb!=0.5.4,!=0.5.5,<0.6.0,>=0.4.0->langchain_chroma) (0.48.9)\n",
      "Requirement already satisfied: tqdm>=4.65.0 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from chromadb!=0.5.4,!=0.5.5,<0.6.0,>=0.4.0->langchain_chroma) (4.66.2)\n",
      "Requirement already satisfied: overrides>=7.3.1 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from chromadb!=0.5.4,!=0.5.5,<0.6.0,>=0.4.0->langchain_chroma) (7.7.0)\n",
      "Requirement already satisfied: importlib-resources in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from chromadb!=0.5.4,!=0.5.5,<0.6.0,>=0.4.0->langchain_chroma) (6.4.0)\n",
      "Requirement already satisfied: grpcio>=1.58.0 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from chromadb!=0.5.4,!=0.5.5,<0.6.0,>=0.4.0->langchain_chroma) (1.62.1)\n",
      "Requirement already satisfied: bcrypt>=4.0.1 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from chromadb!=0.5.4,!=0.5.5,<0.6.0,>=0.4.0->langchain_chroma) (4.1.2)\n",
      "Requirement already satisfied: typer>=0.9.0 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from chromadb!=0.5.4,!=0.5.5,<0.6.0,>=0.4.0->langchain_chroma) (0.12.5)\n",
      "Requirement already satisfied: kubernetes>=28.1.0 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from chromadb!=0.5.4,!=0.5.5,<0.6.0,>=0.4.0->langchain_chroma) (29.0.0)\n",
      "Requirement already satisfied: mmh3>=4.0.1 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from chromadb!=0.5.4,!=0.5.5,<0.6.0,>=0.4.0->langchain_chroma) (4.1.0)\n",
      "Requirement already satisfied: orjson>=3.9.12 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from chromadb!=0.5.4,!=0.5.5,<0.6.0,>=0.4.0->langchain_chroma) (3.9.15)\n",
      "Requirement already satisfied: marshmallow<4.0.0,>=3.18.0 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from dataclasses-json<0.7,>=0.5.7->langchain_community) (3.20.2)\n",
      "Requirement already satisfied: typing-inspect<1,>=0.4.0 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from dataclasses-json<0.7,>=0.5.7->langchain_community) (0.9.0)\n",
      "Requirement already satisfied: starlette<0.37.0,>=0.36.3 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from fastapi<1,>=0.95.2->langchain_chroma) (0.36.3)\n",
      "Requirement already satisfied: jsonpatch<2.0,>=1.33 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from langchain-core<0.3.0,>=0.2.32->langchain) (1.33)\n",
      "Requirement already satisfied: packaging<25,>=23.2 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from langchain-core<0.3.0,>=0.2.32->langchain) (23.2)\n",
      "Requirement already satisfied: annotated-types>=0.4.0 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from pydantic<3,>=1->langchain) (0.6.0)\n",
      "Requirement already satisfied: pydantic-core==2.16.3 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from pydantic<3,>=1->langchain) (2.16.3)\n",
      "Requirement already satisfied: charset-normalizer<4,>=2 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from requests<3,>=2->langchain) (3.3.2)\n",
      "Requirement already satisfied: idna<4,>=2.5 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from requests<3,>=2->langchain) (3.6)\n",
      "Requirement already satisfied: urllib3<3,>=1.21.1 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from requests<3,>=2->langchain) (2.2.2)\n",
      "Requirement already satisfied: certifi>=2017.4.17 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from requests<3,>=2->langchain) (2024.2.2)\n",
      "Requirement already satisfied: greenlet!=0.4.17 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from SQLAlchemy<3,>=1.4->langchain) (3.0.3)\n",
      "Requirement already satisfied: pyproject_hooks in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from build>=1.0.3->chromadb!=0.5.4,!=0.5.5,<0.6.0,>=0.4.0->langchain_chroma) (1.0.0)\n",
      "Requirement already satisfied: tomli>=1.1.0 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from build>=1.0.3->chromadb!=0.5.4,!=0.5.5,<0.6.0,>=0.4.0->langchain_chroma) (2.0.1)\n",
      "Requirement already satisfied: jsonpointer>=1.9 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from jsonpatch<2.0,>=1.33->langchain-core<0.3.0,>=0.2.32->langchain) (2.4)\n",
      "Requirement already satisfied: six>=1.9.0 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from kubernetes>=28.1.0->chromadb!=0.5.4,!=0.5.5,<0.6.0,>=0.4.0->langchain_chroma) (1.16.0)\n",
      "Requirement already satisfied: python-dateutil>=2.5.3 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from kubernetes>=28.1.0->chromadb!=0.5.4,!=0.5.5,<0.6.0,>=0.4.0->langchain_chroma) (2.8.2)\n",
      "Requirement already satisfied: google-auth>=1.0.1 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from kubernetes>=28.1.0->chromadb!=0.5.4,!=0.5.5,<0.6.0,>=0.4.0->langchain_chroma) (2.29.0)\n",
      "Requirement already satisfied: websocket-client!=0.40.0,!=0.41.*,!=0.42.*,>=0.32.0 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from kubernetes>=28.1.0->chromadb!=0.5.4,!=0.5.5,<0.6.0,>=0.4.0->langchain_chroma) (1.7.0)\n",
      "Requirement already satisfied: requests-oauthlib in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from kubernetes>=28.1.0->chromadb!=0.5.4,!=0.5.5,<0.6.0,>=0.4.0->langchain_chroma) (2.0.0)\n",
      "Requirement already satisfied: oauthlib>=3.2.2 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from kubernetes>=28.1.0->chromadb!=0.5.4,!=0.5.5,<0.6.0,>=0.4.0->langchain_chroma) (3.2.2)\n",
      "Requirement already satisfied: coloredlogs in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from onnxruntime>=1.14.1->chromadb!=0.5.4,!=0.5.5,<0.6.0,>=0.4.0->langchain_chroma) (15.0.1)\n",
      "Requirement already satisfied: flatbuffers in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from onnxruntime>=1.14.1->chromadb!=0.5.4,!=0.5.5,<0.6.0,>=0.4.0->langchain_chroma) (24.3.7)\n",
      "Requirement already satisfied: protobuf in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from onnxruntime>=1.14.1->chromadb!=0.5.4,!=0.5.5,<0.6.0,>=0.4.0->langchain_chroma) (4.25.3)\n",
      "Requirement already satisfied: sympy in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from onnxruntime>=1.14.1->chromadb!=0.5.4,!=0.5.5,<0.6.0,>=0.4.0->langchain_chroma) (1.12)\n",
      "Requirement already satisfied: deprecated>=1.2.6 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from opentelemetry-api>=1.2.0->chromadb!=0.5.4,!=0.5.5,<0.6.0,>=0.4.0->langchain_chroma) (1.2.14)\n",
      "Requirement already satisfied: importlib-metadata<7.0,>=6.0 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from opentelemetry-api>=1.2.0->chromadb!=0.5.4,!=0.5.5,<0.6.0,>=0.4.0->langchain_chroma) (6.11.0)\n",
      "Requirement already satisfied: googleapis-common-protos~=1.52 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from opentelemetry-exporter-otlp-proto-grpc>=1.2.0->chromadb!=0.5.4,!=0.5.5,<0.6.0,>=0.4.0->langchain_chroma) (1.63.0)\n",
      "Requirement already satisfied: opentelemetry-exporter-otlp-proto-common==1.23.0 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from opentelemetry-exporter-otlp-proto-grpc>=1.2.0->chromadb!=0.5.4,!=0.5.5,<0.6.0,>=0.4.0->langchain_chroma) (1.23.0)\n",
      "Requirement already satisfied: opentelemetry-proto==1.23.0 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from opentelemetry-exporter-otlp-proto-grpc>=1.2.0->chromadb!=0.5.4,!=0.5.5,<0.6.0,>=0.4.0->langchain_chroma) (1.23.0)\n",
      "Requirement already satisfied: opentelemetry-instrumentation-asgi==0.44b0 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from opentelemetry-instrumentation-fastapi>=0.41b0->chromadb!=0.5.4,!=0.5.5,<0.6.0,>=0.4.0->langchain_chroma) (0.44b0)\n",
      "Requirement already satisfied: opentelemetry-instrumentation==0.44b0 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from opentelemetry-instrumentation-fastapi>=0.41b0->chromadb!=0.5.4,!=0.5.5,<0.6.0,>=0.4.0->langchain_chroma) (0.44b0)\n",
      "Requirement already satisfied: opentelemetry-semantic-conventions==0.44b0 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from opentelemetry-instrumentation-fastapi>=0.41b0->chromadb!=0.5.4,!=0.5.5,<0.6.0,>=0.4.0->langchain_chroma) (0.44b0)\n",
      "Requirement already satisfied: opentelemetry-util-http==0.44b0 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from opentelemetry-instrumentation-fastapi>=0.41b0->chromadb!=0.5.4,!=0.5.5,<0.6.0,>=0.4.0->langchain_chroma) (0.44b0)\n",
      "Requirement already satisfied: setuptools>=16.0 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from opentelemetry-instrumentation==0.44b0->opentelemetry-instrumentation-fastapi>=0.41b0->chromadb!=0.5.4,!=0.5.5,<0.6.0,>=0.4.0->langchain_chroma) (68.2.2)\n",
      "Requirement already satisfied: wrapt<2.0.0,>=1.0.0 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from opentelemetry-instrumentation==0.44b0->opentelemetry-instrumentation-fastapi>=0.41b0->chromadb!=0.5.4,!=0.5.5,<0.6.0,>=0.4.0->langchain_chroma) (1.16.0)\n",
      "Requirement already satisfied: asgiref~=3.0 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from opentelemetry-instrumentation-asgi==0.44b0->opentelemetry-instrumentation-fastapi>=0.41b0->chromadb!=0.5.4,!=0.5.5,<0.6.0,>=0.4.0->langchain_chroma) (3.8.1)\n",
      "Requirement already satisfied: monotonic>=1.5 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from posthog>=2.4.0->chromadb!=0.5.4,!=0.5.5,<0.6.0,>=0.4.0->langchain_chroma) (1.6)\n",
      "Requirement already satisfied: backoff>=1.10.0 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from posthog>=2.4.0->chromadb!=0.5.4,!=0.5.5,<0.6.0,>=0.4.0->langchain_chroma) (2.2.1)\n",
      "Requirement already satisfied: anyio<5,>=3.4.0 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from starlette<0.37.0,>=0.36.3->fastapi<1,>=0.95.2->langchain_chroma) (4.3.0)\n",
      "Requirement already satisfied: huggingface_hub<1.0,>=0.16.4 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from tokenizers>=0.13.2->chromadb!=0.5.4,!=0.5.5,<0.6.0,>=0.4.0->langchain_chroma) (0.21.4)\n",
      "Requirement already satisfied: click>=8.0.0 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from typer>=0.9.0->chromadb!=0.5.4,!=0.5.5,<0.6.0,>=0.4.0->langchain_chroma) (8.1.7)\n",
      "Requirement already satisfied: shellingham>=1.3.0 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from typer>=0.9.0->chromadb!=0.5.4,!=0.5.5,<0.6.0,>=0.4.0->langchain_chroma) (1.5.4)\n",
      "Requirement already satisfied: rich>=10.11.0 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from typer>=0.9.0->chromadb!=0.5.4,!=0.5.5,<0.6.0,>=0.4.0->langchain_chroma) (13.7.1)\n",
      "Requirement already satisfied: mypy-extensions>=0.3.0 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from typing-inspect<1,>=0.4.0->dataclasses-json<0.7,>=0.5.7->langchain_community) (1.0.0)\n",
      "Requirement already satisfied: h11>=0.8 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from uvicorn>=0.18.3->uvicorn[standard]>=0.18.3->chromadb!=0.5.4,!=0.5.5,<0.6.0,>=0.4.0->langchain_chroma) (0.14.0)\n",
      "Requirement already satisfied: httptools>=0.5.0 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from uvicorn[standard]>=0.18.3->chromadb!=0.5.4,!=0.5.5,<0.6.0,>=0.4.0->langchain_chroma) (0.6.1)\n",
      "Requirement already satisfied: python-dotenv>=0.13 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from uvicorn[standard]>=0.18.3->chromadb!=0.5.4,!=0.5.5,<0.6.0,>=0.4.0->langchain_chroma) (1.0.1)\n",
      "Requirement already satisfied: uvloop!=0.15.0,!=0.15.1,>=0.14.0 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from uvicorn[standard]>=0.18.3->chromadb!=0.5.4,!=0.5.5,<0.6.0,>=0.4.0->langchain_chroma) (0.19.0)\n",
      "Requirement already satisfied: watchfiles>=0.13 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from uvicorn[standard]>=0.18.3->chromadb!=0.5.4,!=0.5.5,<0.6.0,>=0.4.0->langchain_chroma) (0.21.0)\n",
      "Requirement already satisfied: websockets>=10.4 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from uvicorn[standard]>=0.18.3->chromadb!=0.5.4,!=0.5.5,<0.6.0,>=0.4.0->langchain_chroma) (11.0.3)\n",
      "Requirement already satisfied: sniffio>=1.1 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from anyio<5,>=3.4.0->starlette<0.37.0,>=0.36.3->fastapi<1,>=0.95.2->langchain_chroma) (1.3.1)\n",
      "Requirement already satisfied: exceptiongroup>=1.0.2 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from anyio<5,>=3.4.0->starlette<0.37.0,>=0.36.3->fastapi<1,>=0.95.2->langchain_chroma) (1.2.0)\n",
      "Requirement already satisfied: cachetools<6.0,>=2.0.0 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from google-auth>=1.0.1->kubernetes>=28.1.0->chromadb!=0.5.4,!=0.5.5,<0.6.0,>=0.4.0->langchain_chroma) (5.3.3)\n",
      "Requirement already satisfied: pyasn1-modules>=0.2.1 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from google-auth>=1.0.1->kubernetes>=28.1.0->chromadb!=0.5.4,!=0.5.5,<0.6.0,>=0.4.0->langchain_chroma) (0.3.0)\n",
      "Requirement already satisfied: rsa<5,>=3.1.4 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from google-auth>=1.0.1->kubernetes>=28.1.0->chromadb!=0.5.4,!=0.5.5,<0.6.0,>=0.4.0->langchain_chroma) (4.9)\n",
      "Requirement already satisfied: filelock in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from huggingface_hub<1.0,>=0.16.4->tokenizers>=0.13.2->chromadb!=0.5.4,!=0.5.5,<0.6.0,>=0.4.0->langchain_chroma) (3.13.1)\n",
      "Requirement already satisfied: fsspec>=2023.5.0 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from huggingface_hub<1.0,>=0.16.4->tokenizers>=0.13.2->chromadb!=0.5.4,!=0.5.5,<0.6.0,>=0.4.0->langchain_chroma) (2024.3.1)\n",
      "Requirement already satisfied: zipp>=0.5 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from importlib-metadata<7.0,>=6.0->opentelemetry-api>=1.2.0->chromadb!=0.5.4,!=0.5.5,<0.6.0,>=0.4.0->langchain_chroma) (3.17.0)\n",
      "Requirement already satisfied: markdown-it-py>=2.2.0 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from rich>=10.11.0->typer>=0.9.0->chromadb!=0.5.4,!=0.5.5,<0.6.0,>=0.4.0->langchain_chroma) (3.0.0)\n",
      "Requirement already satisfied: pygments<3.0.0,>=2.13.0 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from rich>=10.11.0->typer>=0.9.0->chromadb!=0.5.4,!=0.5.5,<0.6.0,>=0.4.0->langchain_chroma) (2.17.2)\n",
      "Requirement already satisfied: humanfriendly>=9.1 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from coloredlogs->onnxruntime>=1.14.1->chromadb!=0.5.4,!=0.5.5,<0.6.0,>=0.4.0->langchain_chroma) (10.0)\n",
      "Requirement already satisfied: mpmath>=0.19 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from sympy->onnxruntime>=1.14.1->chromadb!=0.5.4,!=0.5.5,<0.6.0,>=0.4.0->langchain_chroma) (1.3.0)\n",
      "Requirement already satisfied: mdurl~=0.1 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from markdown-it-py>=2.2.0->rich>=10.11.0->typer>=0.9.0->chromadb!=0.5.4,!=0.5.5,<0.6.0,>=0.4.0->langchain_chroma) (0.1.2)\n",
      "Requirement already satisfied: pyasn1<0.6.0,>=0.4.6 in /home/ubuntu/miniconda3/envs/langchain/lib/python3.10/site-packages (from pyasn1-modules>=0.2.1->google-auth>=1.0.1->kubernetes>=28.1.0->chromadb!=0.5.4,!=0.5.5,<0.6.0,>=0.4.0->langchain_chroma) (0.5.1)\n"
     ]
    }
   ],
   "source": [
    "!pip install langchain langchain_community langchain_chroma"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "697b363d-c56e-420e-b8c1-76420b6cfa46",
   "metadata": {},
   "source": [
    "## **RAG 开发指南**\n",
    "\n",
    "**本指南将详细介绍如何使用 LangChain 框架构建一个基于检索增强生成 (RAG) 的应用。**\n",
    "\n",
    "下面是基于 LangChain 实现的 RAG 的核心步骤与使用到的关键代码抽象（类型、方法、库等）:\n",
    "\n",
    "1. **加载文档**: 使用 `WebBaseLoader` 类从指定来源加载内容，并生成 `Document` 对象（依赖 `bs4` 库）。\n",
    "2. **文档分割**: 使用 `RecursiveCharacterTextSplitter` 类的 `split_documents()` 方法将长文档分割成较小的块。\n",
    "3. **存储嵌入**: 使用 `Chroma` 类的 `from_documents()` 方法将分割后的文档内容嵌入向量空间，并存储在向量数据库中（使用 `OpenAIEmbeddings`），并可以通过检查存储的向量数量来确认存储成功。。\n",
    "4. **检索文档**: 使用 `VectorStoreRetriever` 类的 `as_retriever()` 和 `invoke()` 方法基于查询从向量数据库中检索最相关的文档片段。\n",
    "5. **生成回答**: 使用 `ChatOpenAI` 类的 `invoke()` 方法，将检索到的文档片段与用户问题结合，生成回答（通过 `RunnablePassthrough` 和 `StrOutputParser`）。\n",
    "\n",
    "我们使用的文档是Lilian Weng撰写的《LLM Powered Autonomous Agents》博客文章（https://lilianweng.github.io/posts/2023-06-23-agent/ ），最终构建好的 RAG 应用支持我们询问关于该文章内容的相关问题。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "fd5318a1-2bb9-45bb-8206-82fcf024c4b3",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 导入必要的库\n",
    "import bs4\n",
    "from langchain_community.document_loaders import WebBaseLoader\n",
    "from langchain_text_splitters import RecursiveCharacterTextSplitter\n",
    "from langchain_chroma import Chroma\n",
    "from langchain_openai import OpenAIEmbeddings\n",
    "from langchain_core.output_parsers import StrOutputParser\n",
    "from langchain_core.runnables import RunnablePassthrough\n",
    "from langchain import hub\n",
    "from langchain_core.prompts import ChatPromptTemplate\n",
    "from langchain_openai import ChatOpenAI"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "aebd3284-1cd1-4f53-b105-7f4f5d70faf3",
   "metadata": {},
   "source": [
    "### Step 1: 加载文档\n",
    "\n",
    "- **描述**: 使用 `DocumentLoader` 从指定来源（如网页）加载内容，并将其转换为 `Document` 对象。\n",
    "- **重要代码抽象**:\n",
    "  - 类: `WebBaseLoader`\n",
    "  - 方法: `load()`\n",
    "  - 库: `bs4` (BeautifulSoup)\n",
    "- **代码解释**:\n",
    "  - **文档加载**: 使用 `WebBaseLoader` 从网页加载内容，并通过 `BeautifulSoup` 解析 HTML，提取重要的部分。\n",
    "  - **检查加载数量**: 打印加载的文档数量，确保所有文档正确加载。\n",
    "  - **验证文档内容**: 输出第一个文档的部分内容，确认加载的数据符合预期。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "e6ccef98-31df-4a7d-a5d1-94785651e06d",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 使用 WebBaseLoader 从网页加载内容，并仅保留标题、标题头和文章内容\n",
    "bs4_strainer = bs4.SoupStrainer(class_=(\"post-title\", \"post-header\", \"post-content\"))\n",
    "loader = WebBaseLoader(\n",
    "    web_paths=(\"https://lilianweng.github.io/posts/2023-06-23-agent/\",),\n",
    "    bs_kwargs={\"parse_only\": bs4_strainer},\n",
    ")\n",
    "docs = loader.load()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "0e5c2d11-eff2-43d1-8800-9d9d3a301f39",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "43131\n"
     ]
    }
   ],
   "source": [
    "# 检查加载的文档内容长度\n",
    "print(len(docs[0].page_content))  # 打印第一个文档内容的长度"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "d94fd046-6a2a-4761-a83b-a9f82ed53b63",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "      LLM Powered Autonomous Agents\n",
      "    \n",
      "Date: June 23, 2023  |  Estimated Reading Time: 31 min  |\n"
     ]
    }
   ],
   "source": [
    "# 查看第一个文档（前100字符）\n",
    "print(docs[0].page_content[:100])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "26600ceb-0de8-4820-b438-ed8ae561886f",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "id": "1b6705c5-54ab-4cbd-9718-01563257e54d",
   "metadata": {},
   "source": [
    "### Step 2: 文档分割\n",
    "\n",
    "- **描述**: 使用文本分割器将加载的长文档分割成较小的块，以便嵌入和检索。\n",
    "- **重要代码抽象**:\n",
    "  - 类: `RecursiveCharacterTextSplitter`\n",
    "  - 方法: `split_documents()`\n",
    "- **代码解释**:\n",
    "  - **文档分割**: 使用 `RecursiveCharacterTextSplitter` 按字符大小分割文档块，设置块大小和重叠字符数，确保文档块适合模型处理。\n",
    "  - **检查块数量**: 打印分割后的文档块数量，确保分割操作正确执行。\n",
    "  - **验证块大小**: 输出第一个块的字符数，确认分割块的大小是否符合预期。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "0958c3b5-4110-44b2-9e27-4a82015c0a5e",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 使用 RecursiveCharacterTextSplitter 将文档分割成块，每块1000字符，重叠200字符\n",
    "text_splitter = RecursiveCharacterTextSplitter(\n",
    "    chunk_size=1000, chunk_overlap=200, add_start_index=True\n",
    ")\n",
    "all_splits = text_splitter.split_documents(docs)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "04d73830-8e73-4293-80b0-7e631f7344d1",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "66\n"
     ]
    }
   ],
   "source": [
    "# 检查分割后的块数量和内容\n",
    "print(len(all_splits))  # 打印分割后的文档块数量"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "5263f892-4a80-42b9-b79e-ac3e303a60ca",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "969\n"
     ]
    }
   ],
   "source": [
    "print(len(all_splits[0].page_content))  # 打印第一个块的字符数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "46183c0c-91ec-4609-b7c3-e85c08d8d975",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "LLM Powered Autonomous Agents\n",
      "    \n",
      "Date: June 23, 2023  |  Estimated Reading Time: 31 min  |  Author: Lilian Weng\n",
      "\n",
      "\n",
      "Building agents with LLM (large language model) as its core controller is a cool concept. Several proof-of-concepts demos, such as AutoGPT, GPT-Engineer and BabyAGI, serve as inspiring examples. The potentiality of LLM extends beyond generating well-written copies, stories, essays and programs; it can be framed as a powerful general problem solver.\n",
      "Agent System Overview#\n",
      "In a LLM-powered autonomous agent system, LLM functions as the agent’s brain, complemented by several key components:\n",
      "\n",
      "Planning\n",
      "\n",
      "Subgoal and decomposition: The agent breaks down large tasks into smaller, manageable subgoals, enabling efficient handling of complex tasks.\n",
      "Reflection and refinement: The agent can do self-criticism and self-reflection over past actions, learn from mistakes and refine them for future steps, thereby improving the quality of final results.\n",
      "\n",
      "\n",
      "Memory\n"
     ]
    }
   ],
   "source": [
    "print(all_splits[0].page_content)  # 打印第一个块的内容"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "034ce612-96d0-4b61-8d53-884b48a85a11",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'source': 'https://lilianweng.github.io/posts/2023-06-23-agent/', 'start_index': 8}\n"
     ]
    }
   ],
   "source": [
    "print(all_splits[0].metadata)  # 打印第一个块的元数据"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7b96fb11-41cb-4d79-b7ec-cc294187baf8",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "id": "4d04622f-7e3c-413c-89cd-5eef87949966",
   "metadata": {},
   "source": [
    "### Step 3: 存储嵌入\n",
    "\n",
    "- **描述**: 将分割后的文档内容嵌入到向量空间中，并存储到向量数据库，以便后续检索。\n",
    "- **重要代码抽象**:\n",
    "  - 类: `Chroma`\n",
    "  - 方法: `from_documents()`\n",
    "  - 类: `OpenAIEmbeddings`\n",
    "- **代码解释**:\n",
    "  - **存储嵌入**: 使用 `Chroma.from_documents()` 方法将所有分割的文档片段进行嵌入(`OpenAIEmbeddings`嵌入模型)，将文档片段嵌入向量空间，并存储在向量数据库中。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f39f1eca-7d0c-4bcc-b255-054cc002d7f4",
   "metadata": {},
   "source": [
    "#### Chroma 基础使用\n",
    "\n",
    "**下面是初始化 Chroma 数据库（仅实例化，未存储向量数据）的常见做法：**\n",
    "\n",
    "**使用构造函数初始化**: 在本地持久化存储 Chroma 数据库.\n",
    "\n",
    "```python\n",
    "from langchain_chroma import Chroma\n",
    "\n",
    "vector_store = Chroma(\n",
    "    collection_name=\"example_collection\",\n",
    "    embedding_function=embeddings,\n",
    "    persist_directory=\"./chroma_langchain_db\",  # Where to save data locally, remove if not neccesary\n",
    ")\n",
    "```\n",
    "\n",
    "**使用 Cleint 初始化**: 更方便地访问底层数据库/集合。\n",
    "\n",
    "```python\n",
    "import chromadb\n",
    "\n",
    "persistent_client = chromadb.PersistentClient()\n",
    "collection = persistent_client.get_or_create_collection(\"collection_name\")\n",
    "collection.add(ids=[\"1\", \"2\", \"3\"], documents=[\"a\", \"b\", \"c\"])\n",
    "\n",
    "vector_store_from_client = Chroma(\n",
    "    client=persistent_client,\n",
    "    collection_name=\"collection_name\",\n",
    "    embedding_function=embeddings,\n",
    ")\n",
    "```\n",
    "\n",
    "\n",
    "**我们直接使用 `Chroma.from_documents()` 方法 实例化+数据存储**:\n",
    "\n",
    "该方法返回 Chroma 实例，数据类型为`langchain_chroma.vectorstores.Chroma`，详细 API 文档： https://python.langchain.com/v0.2/api_reference/core/vectorstores/langchain_core.vectorstores.base.VectorStore.html"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "ef288a05-25a4-4f2f-827a-a5f318ff0d7f",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 使用 Chroma 向量存储和 OpenAIEmbeddings 模型，将分割的文档块嵌入并存储\n",
    "vectorstore = Chroma.from_documents(\n",
    "    documents=all_splits,\n",
    "    embedding=OpenAIEmbeddings()\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "39aa3545-b456-473a-b620-5fb0eab15dc2",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "langchain_chroma.vectorstores.Chroma"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 查看 vectorstore 数据类型\n",
    "type(vectorstore) "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "47b52313-646a-4512-8125-68054c1c7e46",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "id": "a6c587ce-b40f-47ad-bd14-480c49a073e7",
   "metadata": {},
   "source": [
    "### Step 4: 检索文档\n",
    "\n",
    "- **描述**: 使用 `VectorStoreRetriever` 类的 `as_retriever()` 和 `invoke()` 方法，从向量数据库中检索与查询最相关的文档片段。\n",
    "- **重要代码抽象**:\n",
    "  - 类: `VectorStoreRetriever`\n",
    "  - 方法: `as_retriever()`, `invoke()`\n",
    "- **代码解释**:\n",
    "  - **文档检索**: 将向量存储转换为检索器，并基于查询执行相似性搜索，获取相关文档片段。\n",
    "  - **检查检索数量**: 打印检索到的文档片段数量，确保检索操作成功。\n",
    "  - **验证检索内容**: 输出第一个检索到的文档内容，确认检索结果与预期相符。\n",
    "\n",
    "在 LangChain 中，所有向量数据库都支持**vectorstore.as_retriever** 方法，实例化该数据库对应的检索器（Retriever），数据类型为`VectorStoreRetriever`，详细 API 文档：https://python.langchain.com/v0.2/api_reference/core/vectorstores/langchain_core.vectorstores.base.VectorStoreRetriever.html"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "0cdab30e-b07d-4273-b734-3aabff356bb5",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 使用 VectorStoreRetriever 从向量存储中检索与查询最相关的文档\n",
    "retriever = vectorstore.as_retriever(search_type=\"similarity\", search_kwargs={\"k\": 6})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "6feddb48-d07e-40be-a9fe-790c3bb75d79",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "langchain_core.vectorstores.base.VectorStoreRetriever"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "type(retriever)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "9283b2e6-874f-4815-915b-e87da0429a5c",
   "metadata": {},
   "outputs": [],
   "source": [
    "retrieved_docs = retriever.invoke(\"What are the approaches to Task Decomposition?\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "0d292f13-9581-4f28-b54d-ba0fe9fa0040",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "6\n"
     ]
    }
   ],
   "source": [
    "# 检查检索到的文档内容\n",
    "print(len(retrieved_docs))  # 打印检索到的文档数量"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "458d6a7d-21a9-49a4-b523-651e3bab51e4",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Tree of Thoughts (Yao et al. 2023) extends CoT by exploring multiple reasoning possibilities at each step. It first decomposes the problem into multiple thought steps and generates multiple thoughts per step, creating a tree structure. The search process can be BFS (breadth-first search) or DFS (depth-first search) with each state evaluated by a classifier (via a prompt) or majority vote.\n",
      "Task decomposition can be done (1) by LLM with simple prompting like \"Steps for XYZ.\\n1.\", \"What are the subgoals for achieving XYZ?\", (2) by using task-specific instructions; e.g. \"Write a story outline.\" for writing a novel, or (3) with human inputs.\n"
     ]
    }
   ],
   "source": [
    "print(retrieved_docs[0].page_content)  # 打印第一个检索到的文档内容"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6cdc6302-1b59-4abc-813a-6b0d44cc7cf2",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "id": "a3309c1e-c650-44ae-baba-6f7528372931",
   "metadata": {},
   "source": [
    "### Step 5: 生成回答\n",
    "\n",
    "- **描述**: 将之前构建的组件（检索器、提示、LLM等）组合成一个完整的链条，实现用户问题的检索与生成回答。完整链条：输入用户问题，检索相关文档，构建提示，将其传递给模型（使用`ChatOpenAI` 类的 `invoke()` 方法），并解析输出生成最终回答。\n",
    "- **重要代码抽象**:\n",
    "  - 类: `ChatOpenAI`\n",
    "  - 方法: `invoke()`\n",
    "  - 类: `RunnablePassthrough`\n",
    "  - 类: `StrOutputParser`\n",
    "  - 模块：`hub`\n",
    "- **代码解释**:\n",
    "  - **模型初始化**: 使用 `ChatOpenAI` 类初始化一个 `GPT-4o-mini` 模型，准备处理生成任务。\n",
    "  - **文档格式化**: 定义 `format_docs` 函数，用于将检索到的文档内容格式化为字符串。\n",
    "  - **构建 RAG 链**: 使用 LCEL (LangChain Execution Layer) 的 `|` 操作符将各个组件连接成一个链条，包括文档检索、提示构建、模型调用以及输出解析。\n",
    "  - **生成回答**: 使用 `stream()` 方法逐步输出生成的回答，并实时展示，确保生成的结果符合预期。\n",
    "\n",
    "![retrieval](../images/retrieval.png)\n",
    "\n",
    "#### LangChain Hub\n",
    "\n",
    "`LangChain Hub` (https://smith.langchain.com/hub) 是一个提示词模板开源社区，为开发者提供了大量开箱即用的提示词模板。属于 `LangSmith` 产品的一部分。\n",
    "\n",
    "下面我们尝试使用 RAG 应用的提示词模板：https://smith.langchain.com/hub/rlm/rag-prompt\n",
    "\n",
    "\n",
    "```\n",
    "You are an assistant for question-answering tasks. Use the following pieces of retrieved context to answer the question. If you don't know the answer, just say that you don't know. Use three sentences maximum and keep the answer concise.\n",
    "Question: {question} \n",
    "Context: {context} \n",
    "Answer:\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "b3dbe88b-7a8f-4607-8426-698a3adf3ef2",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 定义 RAG 链，将用户问题与检索到的文档结合并生成答案\n",
    "llm = ChatOpenAI(model=\"gpt-4o-mini\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "488e8c40-a304-4538-a84e-cc8e4e7e101a",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 使用 hub 模块拉取 rag 提示词模板\n",
    "prompt = hub.pull(\"rlm/rag-prompt\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "cf0de4c1-e389-430b-a472-521960e12b0b",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[HumanMessagePromptTemplate(prompt=PromptTemplate(input_variables=['context', 'question'], template=\"You are an assistant for question-answering tasks. Use the following pieces of retrieved context to answer the question. If you don't know the answer, just say that you don't know. Use three sentences maximum and keep the answer concise.\\nQuestion: {question} \\nContext: {context} \\nAnswer:\"))]\n"
     ]
    }
   ],
   "source": [
    "# 打印模板\n",
    "print(prompt.messages)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "281d2ea1-0a61-45aa-810d-6b80a7d102a2",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 为 context 和 question 填充样例数据，并生成 ChatModel 可用的 Messages\n",
    "example_messages = prompt.invoke(\n",
    "    {\"context\": \"filler context\", \"question\": \"filler question\"}\n",
    ").to_messages()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "0e55e098-5f2d-4ef6-98e2-b1a4f27fa853",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "You are an assistant for question-answering tasks. Use the following pieces of retrieved context to answer the question. If you don't know the answer, just say that you don't know. Use three sentences maximum and keep the answer concise.\n",
      "Question: filler question \n",
      "Context: filler context \n",
      "Answer:\n"
     ]
    }
   ],
   "source": [
    "# 查看提示词\n",
    "print(example_messages[0].content)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "300cbbc6-aaaa-4f8c-8382-1cbda08d2fe9",
   "metadata": {},
   "source": [
    "#### ⭐️**LCEL 在 RAG 中的应用**⭐️\n",
    "\n",
    "##### **LCEL 概述**\n",
    "\n",
    "LCEL 是 LangChain 中的一个重要概念，它提供了一种统一的接口，允许不同的组件（如 `retriever`, `prompt`, `llm` 等）可以通过统一的 `Runnable` 接口连接起来。每个 `Runnable` 组件都实现了相同的方法，如 `.invoke()`、`.stream()` 或 `.batch()`，这使得它们可以通过 `|` 操作符轻松连接。\n",
    "\n",
    "##### **LCEL 中处理的组件**\n",
    "\n",
    "- **Retriever**: 负责根据用户问题检索相关文档。\n",
    "- **Prompt**: 根据检索到的文档构建提示，供模型生成回答。\n",
    "- **LLM**: 接收提示并生成最终的回答。\n",
    "- **StrOutputParser**: 解析 LLM 的输出，只提取字符串内容，供最终显示。\n",
    "\n",
    "##### **LCEL 运作机制**\n",
    "\n",
    "- **构建链条**: 通过 `|` 操作符，我们可以将多个 `Runnable` 组件连接成一个 `RunnableSequence`。LangChain 会自动将一些对象转换为 `Runnable`，如将 `format_docs` 转换为 `RunnableLambda`，将包含 `\"context\"` 和 `\"question\"` 键的字典转换为 `RunnableParallel`。\n",
    "\n",
    "- **数据流动**: 用户输入的问题会在 `RunnableSequence` 中依次经过各个 `Runnable` 组件。首先，问题会通过 `retriever` 检索相关文档，然后通过 `format_docs` 将这些文档转换为字符串。`RunnablePassthrough` 则直接传递原始问题。最后，这些数据被传递给 `prompt` 来生成完整的提示，供 LLM 使用。\n",
    "\n",
    "##### **LCEL 中的关键操作**\n",
    "\n",
    "- **格式化文档**: `retriever | format_docs` 将问题传递给 `retriever` 生成文档对象，然后通过 `format_docs` 将这些文档格式化为字符串。\n",
    "- **传递问题**: `RunnablePassthrough()` 直接传递原始问题，保持原样。\n",
    "- **构建提示**: `{\"context\": retriever | format_docs, \"question\": RunnablePassthrough()} | prompt` 构建完整的提示。\n",
    "- **运行模型**: `prompt | llm | StrOutputParser()` 运行 LLM 生成回答，并解析输出。\n",
    "\n",
    "#### 使用 LCEL 构建 RAG Chain\n",
    "\n",
    "下面我们将 LCEL 的概念与代码实现结合起来，展示了如何通过一系列 `Runnable` 组件来实现完整的 RAG 流程。通过 LCEL，LangChain 提供了高度模块化和可扩展的开发方式，使复杂任务的实现变得更加简单和高效。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "id": "0fe460ec-547e-4714-a7dd-bf9dc319b258",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 定义格式化文档的函数\n",
    "def format_docs(docs):\n",
    "    return \"\\n\\n\".join(doc.page_content for doc in docs)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "id": "66e6049b-af4d-435d-aded-68d9c80803bf",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 使用 LCEL 构建 RAG Chain\n",
    "rag_chain = (\n",
    "    {\"context\": retriever | format_docs, \"question\": RunnablePassthrough()}\n",
    "    | prompt\n",
    "    | llm\n",
    "    | StrOutputParser()\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "id": "e2880d17-79ac-4e5c-966b-144d9ee0dc68",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Task Decomposition is the process of breaking down a complex task into smaller, more manageable steps. This can be achieved through techniques like Chain of Thought (CoT) prompting, which encourages models to think step by step, or by using structured approaches such as Tree of Thoughts. The goal is to simplify the completion of difficult tasks, making them easier to tackle systematically."
     ]
    }
   ],
   "source": [
    "# 流式生成回答\n",
    "for chunk in rag_chain.stream(\"What is Task Decomposition?\"):\n",
    "    print(chunk, end=\"\", flush=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "id": "bb6b9b0e-9883-4375-8bc1-ea31f3659172",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "ToT stands for Tree of Thoughts, a method that extends the Chain of Thought (CoT) approach by exploring multiple reasoning possibilities at each step of problem-solving. It decomposes tasks into multiple thought steps, generating various thoughts per step and organizing them in a tree structure. The search through these thoughts can utilize either breadth-first search (BFS) or depth-first search (DFS) techniques."
     ]
    }
   ],
   "source": [
    "# 流式生成回答\n",
    "for chunk in rag_chain.stream(\"What is ToT?\"):\n",
    "    print(chunk, end=\"\", flush=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5a072d01-60b2-46c7-a104-c8a4a3eda0e0",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "id": "19c3d53d-6ae2-4e05-878b-6702c10995bd",
   "metadata": {},
   "source": [
    "# Homework\n",
    "1. 使用其他的线上文档或离线文件，重新构建向量数据库，尝试提出3个相关问题，测试 LCEL 构建的 RAG Chain 是否能成功召回。\n",
    "2. 重新设计或在 LangChain Hub 上找一个可用的 RAG 提示词模板，测试对比两者的召回率和生成质量。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "14d91a2d-56e0-4f52-b6a3-dc893583b51f",
   "metadata": {},
   "source": [
    "### 自定义 Prompt 的示例"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "id": "94c5d89d-d642-41cb-ab6f-6ead404b6cb9",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_core.prompts import PromptTemplate\n",
    "\n",
    "# 自定义提示词模板\n",
    "template = \"\"\"Use the following pieces of context to answer the question at the end.\n",
    "If you don't know the answer, just say that you don't know, don't try to make up an answer.\n",
    "Use three sentences maximum and keep the answer as concise as possible.\n",
    "Always say \"thanks for asking!\" at the end of the answer.\n",
    "\n",
    "{context}\n",
    "\n",
    "Question: {question}\n",
    "\n",
    "Helpful Answer:\"\"\"\n",
    "\n",
    "custom_rag_prompt = PromptTemplate.from_template(template)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "id": "985266bc-1239-45b1-ab76-d7bd2f279cb4",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Use the following pieces of context to answer the question at the end.\n",
      "If you don't know the answer, just say that you don't know, don't try to make up an answer.\n",
      "Use three sentences maximum and keep the answer as concise as possible.\n",
      "Always say \"thanks for asking!\" at the end of the answer.\n",
      "\n",
      "filler context\n",
      "\n",
      "Question: filler question\n",
      "\n",
      "Helpful Answer:\n"
     ]
    }
   ],
   "source": [
    "# 为 context 和 question 填充样例数据，生成 LLM 可用的提示词\n",
    "print(custom_rag_prompt.invoke({\"context\": \"filler context\", \"question\": \"filler question\"}).text)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "id": "7fbe82ee-de48-4cc8-875c-d74242ef273d",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 重新自定义 RAG Chain\n",
    "custom_rag_chain = (\n",
    "    {\"context\": retriever | format_docs, \"question\": RunnablePassthrough()}\n",
    "    | custom_rag_prompt\n",
    "    | llm\n",
    "    | StrOutputParser()\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "id": "9b818824-5a44-461a-b6d5-3ff079beaaab",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Task decomposition is the process of breaking down a complex task into smaller and more manageable steps, allowing for easier execution and understanding. Techniques such as Chain of Thought (CoT) help enhance model performance by guiding the model to reason step by step. Additionally, the Tree of Thoughts method explores multiple reasoning possibilities at each step, creating a structured approach to problem-solving. Thanks for asking!'"
      ]
     },
     "execution_count": 30,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 使用自定义 prompt 生成回答\n",
    "custom_rag_chain.invoke(\"What is Task Decomposition?\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e6b1fa5d-674d-4e1c-a423-303aea90415b",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.14"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
